package self.zhangkang.chart.service.index

import self.zhangkang.chart.data.CCI
import self.zhangkang.chart.data.KLineInfo
import kotlin.math.abs

/**
 * @author : Android-张康
 * created on: 2023/9/6 16:31
 * description: CCI指标
 * CCI（N日）=（TP－MA）÷MD÷0.015。
 *
 * 其中，TP=（最高价+最低价+收盘价）÷3
 *
 * MA=最近N日收盘价的累计之和÷N
 *
 * MD=最近N日（MA－收盘价）的累计之和÷N
 *
 * 0.015为计算系数，N为计算周期
 */
class CCITools {

    companion object {

        private const val CALCULATION_COEFFICIENT = 0.015F

        private const val CCI_N = 14

        /**
         * 计算 CCI 指标
         */
        fun calculationCCI(dataList: MutableList<KLineInfo>) {
            val kdjTools = CCITools()
            dataList.forEachIndexed { index, kLineInfo ->
                val cci =
                    kdjTools.calculationCCI(
                        dataList = dataList,
                        index = index,
                        n = CCI_N,
                        getTP = {
                            calculationTP(it)
                        },
                        getCloseValue = {
                            it.closePrice
                        })
                dataList[index] = kLineInfo.copy(cci = CCI(cci = cci))
            }
        }

        private fun calculationTP(kLineInfo: KLineInfo): Float {
            //TP=（最高价+最低价+收盘价）÷3
            return (kLineInfo.highPrice + kLineInfo.lowerPrice + kLineInfo.closePrice) / 3.0F
        }
    }

    fun <T> calculationCCI(
        dataList: MutableList<T>,
        index: Int,
        n: Int,
        getTP: (T) -> Float,
        getCloseValue: (T) -> Float
    ): Float {
        //CCI（N日）=（TP－MA）÷MD÷0.015。
        //TP=（最高价+最低价+收盘价）÷3
        //MA=最近N日收盘价的累计之和÷N
        //MD=最近N日（MA－收盘价）的累计之和÷N
        //0.015为计算系数，N为计算周期
        val currentData = dataList[index]
        val TP = getTP(currentData)
        val MA: Float
        val MD: Float
        if (index < n) {
            MA = Float.NaN
            MD = abs(TP)
        } else {
            MA = calculateMovingAverage(dataList, index, n, getTP)
            MD = calculateMD(dataList, index, n, getTP)
        }
        return (TP - MA) / MD / CALCULATION_COEFFICIENT
    }

    private fun <T> calculateMD(
        dataList: List<T>,
        currentIndex: Int,
        n: Int,
        getValue: (T) -> Float
    ): Float {
        //MA=最近N日收盘价的累计之和÷N
        //MD=最近N日（MA－收盘价）的累计之和÷N
        if (currentIndex + 1 < n) {
            return Float.NaN
        }
        val TP_MA: Float = calculateMovingAverage(
            dataList = dataList,
            currentIndex = currentIndex,
            n = n,
            getValue = getValue
        )
        var value = 0F
        for (index in currentIndex - n + 1..currentIndex) {
            value += abs(getValue(dataList[index]) - TP_MA)
        }
        return value.div(n)
    }

    /**
     * 计算移动平均值
     *
     * @param dataList K线数据列表
     * @param currentIndex 当前的位置
     *
     * @return 返回 [currentIndex] 位置的 [n] 日移动平均值
     */
    private fun <T> calculateMovingAverage(
        dataList: List<T>,
        currentIndex: Int,
        n: Int,
        getValue: (T) -> Float
    ): Float {
        if (currentIndex + 1 < n) {
            return Float.NaN
        }
        var value: Float = getValue(dataList[currentIndex])
        for (index in currentIndex - n + 1 until currentIndex) {
            value += getValue(dataList[index])
        }
        return value.div(n)
    }

}